// 若要启用实验性的装饰器特性，你必须在命令行或tsconfig.json里启用编译器选项
// "experimentalDecorators": true,
// 装饰器是一种特殊类型的声明，它能够被附加到类声明，方法， 访问符，属性或参数上。

class A1 {
  constructor() {}
}
// 定义一个类装饰器函数 他会把ClassA的构造函数传入你的watcher函数当做第一个参数
const watcher: ClassDecorator = (target: Function) => {
  target.prototype.getParams = <T>(params: T): T => {
    return params
  }
}
// 使用的时候 直接通过@函数名使用
@watcher
class A2 {
  constructor() {}
}

const aa2 = new A2()
console.log((aa2 as any).getParams('123'))

// ps2
// 其实也就是一个高阶函数 外层的函数接受值 里层的函数最终接受类的构造函数
const watcher1 = (name: string): ClassDecorator => {
  return (target: Function) => {
    target.prototype.getParams = <T>(params: T): T => {
      return params
    }
    target.prototype.getOptions = (): string => {
      return name
    }
  }
}

@watcher1('name')
class AA3 {
  constructor() {}
}

const a3 = new AA3()
console.log((a3 as any).getParams('123'))

//ps 3
// 可以使用多个装饰器
const watcher4 = (name: string): ClassDecorator => {
  return (target: Function) => {
    target.prototype.getParams = <T>(params: T): T => {
      return params
    }
    target.prototype.getOptions = (): string => {
      return name
    }
  }
}
const watcher41 = (name: string): ClassDecorator => {
  return (target: Function) => {
    target.prototype.getNames = (): string => {
      return name
    }
  }
}

@watcher4('name2')
@watcher41('name')
class A4A {
  constructor() {}
}

const a4a = new A4A()
console.log((a4a as any).getOptions())
console.log((a4a as any).getNames())

//ps4
/* 方法装饰器
返回三个参数

对于静态成员来说是类的构造函数，对于实例成员是类的原型对象。
成员的名字。
成员的属性描述符。 */

/* [
  {},
  'setParasm',
  {
    value: [Function: setParasm],
    writable: true,
    enumerable: false,
    configurable: true
  }
] */

const met: MethodDecorator = (...args) => {
  console.log(args)
}

class Amet{
  constructor() {}
  @met
  getName(): string {
    return '小满'
  }
}

const amet = new Amet()


//ps5 参数装饰器
/*
返回三个参数

对于静态成员来说是类的构造函数，对于实例成员是类的原型对象。
成员的名字。
参数在函数参数列表中的索引。
[ {}, 'setParasm', 0 ] */
const metP:ParameterDecorator = (...args) => {
  console.log(args);
}

class AmetP {
  constructor() {

  }
  setParasm (@metP name:string = '213') {

  }
}
const ametP = new AmetP();